﻿namespace IBTrader.Modules.Strategy
{
    using IBTrader.Model;
using IBTrader.Modules.Market;
using IBTrader.Modules.Read;
using IBTrader.Modules.Tws;
using NLog;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;

    abstract class SingleStock
    {
        public event EventHandler<Line> Up, Down;
        public event EventHandler<OrderLine> Enter, Exit;
        #region private variables
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
        private Line bid, ask;
        private OrderLine order;
        private Algo algo;
        private readonly Store store;
        private readonly Market.Order market;
        private readonly int algoId;
        private readonly LogLevel logLevel = LogLevel.Trace;
        #endregion
        public SingleStock(Store store, Market.Order market, int algoId)
        {
            this.store = store;
            this.market = market;
            this.algoId = algoId;
        }
        #region states
        private Algo Algo { get { return algo ?? (algo = store.Algos.Single(a => a.Id == algoId)); } }
        private static readonly Action<bool, Action> Action = (b, a) => { if (b) a(); };
        private bool IsUp(Line line, Line last, double off) 
        {
            if (Up != null) Up(this, last);
            logger.Log(logLevel, "{0:0.00} {1:0.00} {2:0.00}", last.Price, line.Price, last.Price - line.Price - off);
            return last.Price > line.Price + off; 
        }
        private bool IsDown(Line line, Line last, double off) 
        {
            if (Down != null) Down(this, last);
            logger.Log(logLevel, "{0:0.00} {1:0.00} {2:0.00}", last.Price, line.Price, -last.Price + line.Price - off);
            return last.Price < line.Price - off; 
        }
        private bool Ordered { get { return Algo.OrderingStatus == OrderingStatus.Bought; } }
        private bool NotOrdered { get { return Algo.OrderingStatus == OrderingStatus.None; } }
        #endregion
        #region actions
        private void Place(Line line, DateTime dt, bool up)
        {
            logger.Log(logLevel, "::Place {0} {1} {2}", Algo.Contract.Symbol, up, line);
            order = new OrderLine { DateTime = dt, Algo = Algo, Price = line.Price, Up = up };
            if (Enter != null) Enter(this, order);
        }
        private void End(Line line, DateTime dt, bool stop = false)
        {
            logger.Log(logLevel, "::End {0} {1} {2}", Algo.Contract.Symbol, stop, line);
            order = new OrderLine { DateTime = dt, Algo = Algo, Price = (Ordered && this.order.Up ? ask : bid).Price, Up = !this.order.Up };
            if (Exit != null) Exit(this, order);
        }
        #endregion
        #region events
        public void Bid(object _, Line line)
        {
            bid = line;
        }
        public void Ask(object _, Line line)
        {
            ask = line;
        }
        protected abstract bool WithTrend { get; }
        StreamWriter test = File.CreateText("test.txt");
        public void Hit(object _, Line line)
        {
            if (bid != null)
            {
                if (NotOrdered)
                {
                    Action(IsUp(line, bid, Algo.PriceOffset), () => Place(bid, line.DateTime, WithTrend));
                }
                if (Ordered && order.Up != WithTrend)
                {
                    Action(IsUp(line, bid, Algo.PriceOffset), () => End(bid, line.DateTime));
                    Action(IsDown(line, bid, Algo.StopLimit), () => End(bid, line.DateTime, true));
                }
            }
            if (ask != null)
            {
                if (NotOrdered)
                {
                    Action(IsDown(line, ask, Algo.PriceOffset), () => Place(ask, line.DateTime, !WithTrend));
                }
                if (Ordered && order.Up == WithTrend)
                {
                    Action(IsDown(line, ask, Algo.PriceOffset), () => End(ask, line.DateTime));
                    Action(IsUp(line, ask, Algo.StopLimit), () => End(ask, line.DateTime, true));
                }
            }
        }
        #endregion
    }
}